#include "toolkits/includes/glew.h"
#include "terrain.h"
#include "texture1.h"
#include "definitions.h"
#include "toolkits/includes/glut.h"
#include "toolkits/includes/il.h"
#include <math.h>
#include <string.h>


extern unsigned int texID;
extern ILint heightH, heightW;
extern unsigned char *texData;
extern float *heightData;
extern float heightMap[TERRAINLENGTH*2][TERRAINLENGTH*2];
extern Texture1 *textureClass;
extern float minHeight;

extern GLuint buffers[2];
extern float *vertexB;
extern float *textB;
int gridStrips[(TERRAINLENGTH*2)-1][TERRAINHEIGHT*2]; 

Terrain::Terrain() {
}

float Terrain::getHeight(float x, float z) { // get height
  return heightData[heightH*(int)x + ((int)z)];  
}

void Terrain::heightedVertex(float mult, float x, float z) {
  glVertex3f(mult*x, getHeight(x, z), mult*z);
}

void Terrain::drawTerrain() {
	float color[] = {1.0,1.0,1.0,1.0};
  glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE, color);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texID);
  float x, z, stepPt = (TERRAINLENGTH*2)/10.0f;
  float y = 0.0f;

  glPushMatrix();	

  for (z= (-TERRAINLENGTH); (z) <= (TERRAINLENGTH); z += stepPt){
		glBegin(GL_TRIANGLE_STRIP);
		for (x= (-TERRAINLENGTH); (x) <= (TERRAINLENGTH); x += stepPt){
      glTexCoord2f(x, z);
      y = heightMap[(int)x+TERRAINLENGTH][(int)z+TERRAINLENGTH];
      if(y < 0.0f || y > TERRAINHEIGHT) {
        y = minHeight; // se altura negativa ou maior do que a altura do terreno
        // aproveita-se e actualiza-se o mapa de alturas
        heightMap[(int)x+TERRAINLENGTH][(int)z+TERRAINLENGTH] = y;
      }
      glVertex3f((float)x, y, (float)z);
      glTexCoord2f(x, z+stepPt);
      y = heightMap[(int)x+TERRAINLENGTH][(int)(z+stepPt)+TERRAINLENGTH];
      if(y < 0.0f || y > TERRAINHEIGHT) {
        y = minHeight; // se altura negativa ou maior do que a altura do terreno
        // aproveita-se e actualiza-se o mapa de alturas
        heightMap[(int)x+TERRAINLENGTH][(int)(z+stepPt)+TERRAINLENGTH] = y;
      }
      glVertex3f((float)x, y, (float)z+stepPt);
		}
		glEnd();
	}

  /*glBegin(GL_TRIANGLE_STRIP);
  for (z= (-TERRAINLENGTH); (z) <= (TERRAINLENGTH); z += stepPt){
      glTexCoord2f(x, z);
      y = heightMap[(int)x][(int)z+TERRAINLENGTH];

      if(y < 0.0f || y > TERRAINHEIGHT) {
        y = minHeight; // se altura negativa ou maior do que a altura do terreno
        // aproveita-se e actualiza-se o mapa de alturas
        heightMap[(int)x][(int)z+TERRAINLENGTH] = y;
      }
      
      glVertex3f((float)x, y, (float)z);
      glTexCoord2f(x-stepPt, z);
      y = heightMap[(int)(x-stepPt)][(int)(z)+TERRAINLENGTH];

      if(y < 0.0f || y > TERRAINHEIGHT) {
        y = minHeight; // se altura negativa ou maior do que a altura do terreno
        // aproveita-se e actualiza-se o mapa de alturas

        heightMap[(int)(x-stepPt)][(int)(z)+TERRAINLENGTH] = y;
      }
      glVertex3f((float)(x-stepPt), y, (float)z);
  }
  glEnd();*/
  glPopMatrix();
 
	glBindTexture(GL_TEXTURE_2D, 0);
}

void Terrain::drawTerrainVBO() {
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, texID);

  glBindBuffer(GL_ARRAY_BUFFER,buffers[1]);
  glTexCoordPointer(2,GL_FLOAT,0,0);

	glBindBuffer(GL_ARRAY_BUFFER,buffers[0]);
	glVertexPointer(3,GL_FLOAT,0,0);

  int i;

  for(i = 0; i < (TERRAINLENGTH*2)-1; i++) {
    glDrawElements(GL_TRIANGLE_STRIP, (TERRAINLENGTH*2)*2, GL_UNSIGNED_INT, gridStrips[i]);
  }

  glDisable(GL_TEXTURE_2D);
}

void Terrain::generateVBO() {
  /////////////////////
	// maybe it's not necessary!
  glewInit();
	glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  ////
  int terrainSize = TERRAINLENGTH * 2;
  int size = terrainSize * terrainSize * 3;
  
  gridStrips[(TERRAINLENGTH*2)-1][TERRAINHEIGHT*2];
  vertexB = new float[size];
  textB = new float[terrainSize * terrainSize * 2];

  int z, x, pos = 0, tex = 0, stepPt = (TERRAINLENGTH*2)/10;
  int nVertices = 0, nStrips = 0;

  for (z= -TERRAINLENGTH; z < TERRAINLENGTH; z+=stepPt) {
		for (x= -TERRAINLENGTH; x < TERRAINLENGTH; x+=stepPt) {
      vertexB[pos++] = (float)x;
      vertexB[pos++] = heightMap[x + TERRAINLENGTH][z + TERRAINLENGTH];
      vertexB[pos++] = (float)z;
      textB[tex++] = (float)x;
      textB[tex++] = (float)z;
      nVertices+=3; 
		}
	}
  
  printf("Vertices = %d\n", nVertices);

  int i = 0;

  for(z = 0; z <= (terrainSize/stepPt); z++) {
    pos = 0;
    for(x = 0; x <= (terrainSize/stepPt)*3; x++) {
      gridStrips[z][pos++] = x + (z+1) * ((terrainSize/stepPt)*3);
      gridStrips[z][pos++] = x + z * ((terrainSize/stepPt)*3);
    }
  }

  // generate a new VBO an  d get the associated ID
	glGenBuffers(2, buffers);

	// bind VBO in order to use
	glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);

	// upload data to VBO
	glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), vertexB, GL_STATIC_DRAW);	

	glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
	glBufferData(GL_ARRAY_BUFFER, terrainSize * terrainSize * sizeof(float) * 2, textB, GL_STATIC_DRAW);

  delete [] textB;
  delete [] vertexB;

  
}
